<!--
 * @Author: 四川华西绿舍建材有限公司
 * @Date: 2024-01-25 17:22:24
 * @LastEditors: 陈波
 * @LastEditTime: 2024-05-06 16:10:18
 * @Description: 
 * @FilePath: \graDesign\src\views\Tentative\index.vue
-->
<template>
  <div>
    <!-- <LayoutIndex>
      <el-button type="primary" plain @click="sendReq">发起请求</el-button>
      <el-button type="danger" plain @click="cancelReq">取消请求</el-button>
    </LayoutIndex> -->
    <!-- <LayoutDetail>
      <h1>大文件上传</h1>
      <input type="file" @change="handleUpload" />
    </LayoutDetail> -->
    <LayoutDetail>
      <SalesInvoice />
    </LayoutDetail>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { onBeforeRouteLeave } from 'vue-router';
import SparkMD5 from "spark-md5";
import testApi from "@/request/api/testApi";
// import axios from "axios";
import SalesInvoice from "./components/SalesInvoice.vue";

let controller = null;
const sendReq = () => {
  controller = new AbortController();
  testApi.testMockHttp(controller);
  // axios.get('http://localhost:3000/mock', {
  //   signal: controller.signal
  // })
}

const cancelReq = () => {
  // 取消请求
  controller.abort()
}

onBeforeRouteLeave((to, from, next) => {
  if (controller) {
    controller.abort();
  }
  next();
})


const CHUNK_SIZE = 1024 * 1024; // 1MB
const fileName = ref(""); // 文件名称
const fileHash = ref(""); // 文件hash

// 创建文件分片
const createChunks = (file: File) => {
  let start = 0;
  const chunks = [];
  while (start < file.size) {
    chunks.push(file.slice(start, start + CHUNK_SIZE));
    start += CHUNK_SIZE;
  }
  return chunks;
};

// 计算文件内容hash值
const calculateHash = (file: File): Promise<string> => {
  return new Promise((resolve) => {
    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(file);
    fileReader.onload = function (e) {
      const spark = new SparkMD5.ArrayBuffer();
      spark.append((e.target as FileReader).result as ArrayBuffer);
      resolve(spark.end());
    };
  });
};

// 控制请求并发
const concurRequest = (
  taskPool: Array<() => Promise<Response>>,
  max: number
): Promise<Array<Response | unknown>> => {
  return new Promise((resolve) => {
    if (taskPool.length === 0) {
      resolve([]);
      return;
    }

    const results: Array<Response | unknown> = [];
    let index = 0;
    let count = 0;

    const request = async () => {
      if (index === taskPool.length) return;
      const i = index;
      const task = taskPool[index];
      index++;
      try {
        results[i] = await task();
      } catch (err) {
        results[i] = err;
      } finally {
        count++;
        if (count === taskPool.length) {
          resolve(results);
        }
        request();
      }
    };

    const times = Math.min(max, taskPool.length);
    for (let i = 0; i < times; i++) {
      request();
    }
  });
};

// 合并分片请求
const mergeRequest = () => {
  fetch("http://localhost:3000/merge", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      fileHash: fileHash.value,
      fileName: fileName.value,
    }),
  });
};

// 上传文件分片
const uploadChunks = async (
  chunks: Array<Blob>,
  existChunks: Array<string>
) => {
  const formDatas = chunks
    .map((chunk, index) => ({
      fileHash: fileHash.value,
      chunkHash: fileHash.value + "-" + index,
      chunk,
    }))
    .filter((item) => !existChunks.includes(item.chunkHash))
    .map((item) => {
      const formData = new FormData();
      formData.append("fileHash", item.fileHash);
      formData.append("chunkHash", item.chunkHash);
      formData.append("chunk", item.chunk);
      return formData;
    });

  const taskPool = formDatas.map(
    (formData) => () =>
      fetch("http://localhost:3000/upload", {
        method: "POST",
        body: formData,
      })
  );

  // 控制请求并发
  await concurRequest(taskPool, 6);

  // 合并分片请求
  mergeRequest();
};

// 校验文件、文件分片是否存在
const verify = (fileHash: string, fileName: string) => {
  return fetch("http://localhost:3000/verify", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      fileHash,
      fileName,
    }),
  }).then((res) => res.json());
};

// 绑定上传事件
const handleUpload = async (e: Event) => {
  const file = (e.target as HTMLInputElement).files?.[0];
  if (!file) return;

  fileName.value = file.name;

  // 创建文件分片
  const chunks = createChunks(file);
  console.log('chunks:', chunks)

  // 计算文件内容hash值
  fileHash.value = await calculateHash(file);

  // 校验文件、文件分片是否存在
  const verifyRes = await verify(fileHash.value, fileName.value);
  const { existFile, existChunks } = verifyRes.data;
  if (existFile) return;

  // 上传文件分片
  uploadChunks(chunks, existChunks);
};
</script>